"""
класс connSQL - обеспечивает процедуру соединения с сервером
класс fmServer - модальная форма (параметры соединения с сервером)
"""

import os
import sys
import configparser

from PyQt5.QtSql import *
from PyQt5.QtWidgets import *
# from PyQt5.QtGui import *


def conn_sql(ini_file, driver="QMYSQL", conn_name="one"):
    """
    Соединение с БД. 

    Вход: ini файл, имя драйвера, имя соединения.
    Выход: ссылка на БД.

    Поддерживает множественные соединения с разными именами (conn_name )
    и с разными типами SQL серверов (driver). Параметры соединения сохраняются 
    в ini файле. Имеет встроенное диалоговое окно для ввода параметров соединения.
    """
    # ЧТЕНИЕ INI ФАЙЛА
    # Создаем переменную парсера ini файла. Формируем полный путь до ini
    # файла. Читаем ini файл. Отсутствие ini файла или нужной секции
    # равнозначно, поэтому достаточно проверить наличие секции в словаре.
    # Если секции SERVER нет, то добовляем с пустыми значениями.

    config = configparser.ConfigParser()
    ini_file = "{}\\{}".format(os.getenv('APPDATA'), ini_file)
    config.read(ini_file)
    if not ('SERVER' in config):
        config['SERVER'] = {'host_name': '',
            'data_base':'', 'user':'','password':''}

    # Открывает БД. Если удача, возвращаем указатель.
    # Если облом, открываем модальную форму для ручной настройки.
    # Если ручная настройка удачна, записывает конфиг и возвращаем
    # указатель на базу данных, иначе закрываем все соединения.
    
    db = open_db(config, driver, conn_name)
    if db:
        return db
        
    dialog = DialogServer(config, driver, conn_name)
    result = dialog.exec_()
    if result == QDialog.Accepted:
        with open(ini_file, 'w') as file_ini:
            config.write(file_ini)
        return dialog.db
    else:
        QSqlDatabase.close()


class DialogServer(QDialog):
    """
    Форма ручного соединения с сервером.
    Вход: параметры соединения.
    """

    def __init__(self, config, driver, conn_name):
        super().__init__()

        self.config = config
        self.driver = driver
        self.conn_name = conn_name
        self.setup_ui()
        
    def setup_ui(self):    
        # ВИДЖЕТЫ
        # Записываем параметры соединения в виджеты полей.
        # Нарушение структуры секции в ini файле устраняется функцией get().
        server = config['SERVER']
        self.edt_host_name = QLineEdit(server.get('host_name',''))
        self.edt_database = QLineEdit(server.get('data_base',''))
        self.edt_user_name = QLineEdit(server.get('user',''))
        self.edt_password = QLineEdit(server.get('password',''))
        btn_open_db = QPushButton('ОТКРЫТЬ БАЗУ ДАННЫХ')
        btn_open_db.clicked.connect(self.open_db)

        # ФОРМА
        # Добавлем к форме все виджеты.
        form = QFormLayout()
        form.addRow("Сервер", self.edt_host_name)
        form.addRow("База", self.edt_database)
        form.addRow("Логин", self.edt_user_name)
        form.addRow("Пароль", self.edt_password)
        form.addRow(btn_open_db)
        self.setLayout(form)
        self.setWindowTitle("ПАРАМЕТРЫ БД")
        self.setMinimumWidth(200)


    def open_db(self):
        """
        Проверка заполнения полей. Загрузка значений полей в переменную конфигурации.
        Попытка соединения с сервером. Закрытие формы в случае удачного соединения
        """
        # ПРОВЕРКА ЗАПОЛЕНИЯ ПОЛЕЙ
        if (not self.edt_host_name.text() or                                    
            not self.edt_database.text() or
            not self.edt_user_name.text() or
            not self.edt_password.text()):
            dialog = QMessageBox()
            dialog.setIcon(QMessageBox.Warning)                                 
            dialog.setWindowTitle("НЕДОСТАТОЧНО ИНФОРМАЦИИ")
            dialog.setText("Все поля должны быть заполнены.")
            dialog.exec_()
            return
            
        # СОХРАНЕНИЕ КОНФИГУРАЦИИ
        server = self.config['SERVER']                                          
        server['host_name'] = self.edt_host_name.text()                        
        server['data_base'] = self.edt_database.text()
        server['user'] = self.edt_user_name.text()
        server['password'] = self.edt_password.text()
            
        # СОЕДИНЕНИЕ С БД
        db = open_db(self.config, self.driver, self.conn_name) 
        if db:
            self.db = db
            self.accept()

    def closeEvent(self,е):
        ignore()
        self.ignore()


def open_db(config, driver, conn_name):
    '''
    Открывает базу данных, использую параметры из configparser. Если процедура
    успешна, возвражает указатель на базу, иначе выводит сообщение об ошибке.
    '''

    # СОЕДИНЕНИЕ С СЕВЕРОМ
    # Сначала создается соединение с сервером. Возможно уже была попытка
    # открыть базу и соединение существет. Eсли соединение уже активно,
    # вытаскиваем существущую ссылку на соединение, иначе будет сообщение об
    # ошибке.

    if QSqlDatabase.contains(conn_name):
        db = QSqlDatabase.database(conn_name)
    else:
        db = QSqlDatabase.addDatabase(driver, conn_name)              

    # СОЕДИНЕНИЕ С БД
    # Затем соединяемся с БД. Грузим параметры соединения из config.
    # Открываем БД. Если удача, возвращаем указатель, иначе сообщение.

    server = config['SERVER']
    print(server.get('host_name'))
    db.setHostName(server.get('host_name'))
    db.setDatabaseName(server.get('data_base'))
    db.setUserName(server.get('user'))
    db.setPassword(server.get('password'))

    if db.open():                                                               
        return db
    else:
        error = db.lastError()
        num_error = error.number()
        if num_error == 1044:
            message = "Введите правильное имя базы данных или обратитесь к системному администратору."
        elif num_error == 1045:
            message = "Введите правильное имя пользователя и/или пароль или обратитесь к системному администратору."
        elif num_error in (2003, 2005):
            message = "Введите правильный IP адрес или название сервера или обратитесь к системному администратору."
        else:
            message = ("Код ошибки сервера: {}. Оригинальное сообщение: {}. Обратитесь к программисту."
                .format(num_error,error.text()))
        dialog = QMessageBox()
        dialog.setIcon(QMessageBox.Critical)                                  
        dialog.setWindowTitle("ОШИБКА СОЕДИНЕНИЯ С БАЗОЙ ДАННЫХ")
        dialog.setText(message)
        dialog.exec_()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    D = connSQL("test.ini")
    db = D.connect()
    if db:
        print("OK")
